package msg

import (
	"fmt"
	"github.com/golang/protobuf/proto"
	"leaf/core/log"
	"reflect"
	"sync"
)

var msgRid uint32 = 1
var msgLock sync.Mutex

const (
	MessageHead_Flow_Len = 9
)

var (
	Error_MessageParse_MsgId   = fmt.Errorf("msg id error")
	Error_MessageParse_MsgType = fmt.Errorf("msg type error")
)

func GetMessageRid() uint32 {
	msgLock.Lock()
	defer msgLock.Unlock()
	msgRid++
	if msgRid == 0 {
		msgRid++
	}
	return msgRid
}

type MessageType byte

type PMsg struct {
	Request *Message
	Reply   *Message
}

func (msg *PMsg) C2S() interface{} {
	if msg.Request != nil {
		return msg.Request.Msg
	}
	return nil
}

func (msg *PMsg) S2C() interface{} {
	if msg.Reply != nil {
		return msg.Reply.Msg
	}
	return nil
}

type MessageHead struct {
	Type      MessageType
	Index     uint32
	Code      uint16
	MsgId     uint16
	RpcLen    uint16
	RpcMethod string
}

type Message struct {
	MessageHead
	Bytes []byte
	Msg   proto.Message
}

func (msg *Message) Parse(head *MessageHead, bytes []byte) error {
	msg.MessageHead = *head
	if msg.MsgId == 0 {
		if msg.Code == 0 {
			return Error_MessageParse_MsgId
		}
		return nil
	}

	msgType := GetMessageType(msg.MsgId)
	if msgType != nil && msgType != reflect.TypeOf(msg.Msg) {
		return Error_MessageParse_MsgType
	}

	if err := proto.Unmarshal(bytes, msg.Msg); err != nil {
		return err
	}
	return nil
}

func NewMessageFromBytes(head *MessageHead, bytes []byte) *Message {
	msg := new(Message)
	msg.MessageHead = *head
	if msg.MsgId == 0 || msg.Code != 0 {
		return msg
	}
	msgType := GetMessageType(msg.MsgId)
	if msgType == nil {
		log.Error("NewMessageFromBytes err:msgType is nil,msgid:%v", msg.MsgId)
		return nil
	}

	var inf interface{}
	if msgType.Kind() == reflect.Ptr {
		inf = reflect.New(msgType.Elem()).Interface()
	} else {
		inf = reflect.New(msgType).Interface()
	}

	protoMsg, ok := inf.(proto.Message)
	if !ok {
		log.Error("NewMessageFromBytes err:msg  nil,msgid:%v", msg.MsgId)
		return nil
	}
	if err := proto.Unmarshal(bytes, protoMsg); err != nil {
		log.Error("NewMessageFromBytes err:msg  nil,msgid:%v", err, msg.MsgId)
		return nil
	}
	msg.Msg = protoMsg
	return msg
}

func NewMessage(id uint32, data proto.Message) *Message {
	msg := new(Message)
	msg.Type = MessageType_Msg
	msg.MsgId = GetMessageId(reflect.TypeOf(data))
	if msg.MsgId == 0 {
		log.Error("NewMessage err:not find msg, msgName:%v", reflect.TypeOf(data).Name())
		return nil
	}
	msg.Index = id
	msg.Msg = data
	return msg
}

func NewRpcMessage(method string, id uint32, message proto.Message) *Message {
	msg := new(Message)
	msg.Type = MessageType_Rpc
	msg.Index = id
	msg.RpcMethod = method
	msg.RpcLen = uint16(len(method))
	if message != nil {
		msg.MsgId = GetMessageId(reflect.TypeOf(message))
		msg.Msg = message
	}
	return msg
}
